package uk.ac.cam.cl.projectcharlie2010.engines;

import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Logger;

import ch.nostromo.tiffanys.engines.EngineListener;
import ch.nostromo.tiffanys.engines.EngineResult;
import ch.nostromo.tiffanys.engines.EngineSettings;
import ch.nostromo.tiffanys.engines.EngineTimer;
import ch.nostromo.tiffanys.game.board.Board;

public abstract class Engine extends Thread {
	static Logger logger = Logger.getLogger(Engine.class.getName());

	public static final int MAX_RANGE = 99999;

	public static final int MAX_MATE_DEPTH = 999;

	public static final int MAX_MATE_RANGE = MAX_RANGE - MAX_MATE_DEPTH;

	private Vector<EngineListener> listener;

	private Board board;

	private int colorToMove;

	private String playedPGN;

	protected EngineSettings engineSettings = null;

	public boolean running = true;

	public abstract EngineResult engineSelectNextMove(Board parBoard, int parColorToMove, String playedPGN2);

	public abstract String getEngineName();

	EngineResult engineResult = null;

	EngineTimer et = null;
	
	private boolean active = true;
	
	//NEW 
	private boolean alive = true;

	public void setEngineSearchParameters(Board board, int colorToMove, String playedPGN) {
		this.board = board;
		this.colorToMove = colorToMove;
		this.playedPGN = playedPGN;
	}

	public void startEngine() {
		System.out.println("ENGINE STARTED:" + this.toString());
		// Check for timing

		if (et != null) {
			et.stopRunnning();
		}

		if (engineSettings.GAME_MODE == EngineSettings.GAME_MODE_FIXED_TIME_PER_MOVES) {
			double secPerMove = (double) engineSettings.FIXED_TIME_PER_MOVES_SECONDS /  (double) engineSettings.FIXED_TIME_PER_MOVES_MOVE_COUNT;
			secPerMove -= engineSettings.TIME_HOLE;
			logger.info("Starting timer with : " + secPerMove);
			et = new EngineTimer(this, secPerMove);
			et.start();
			start();
		} else if (engineSettings.GAME_MODE == EngineSettings.GAME_MODE_FIXED_TIME_PER_MOVE) {
			double secPerMove = engineSettings.FIXED_TIME_PER_MOVE;
			secPerMove -= engineSettings.TIME_HOLE;
			et = new EngineTimer(this, secPerMove);
			et.start();
			start();
		} else {
			start();
		}
	}

	/** Run method. A bit complicated, cause suspend() and resume() are depricated */
	@Override
	public void run() {
		running = true;
		engineResult = engineSelectNextMove(board, colorToMove, playedPGN);
		checkActive();
		if (alive) {
			processEngineFinishedEvent(engineResult);
		} else {
			//TODO RAT
			System.out.println("Engine dying: " + this);
		}
	}

	public synchronized void addEngineListener(EngineListener m) {
		if (listener == null) {
			listener = new Vector<EngineListener>(5, 5);
		}
		if (!listener.contains(m)) {
			listener.addElement(m);
		}
	}

	private synchronized void processEngineFinishedEvent(EngineResult engineResult) {
		if (listener != null) {
			for (Enumeration<EngineListener> e = listener.elements(); e.hasMoreElements();) {
				(e.nextElement()).engineFinished(engineResult);
			}
		}
	}
	protected synchronized void processDepthFinished(EngineResult engineResult) {
		if (listener != null) {
			for (Enumeration<EngineListener> e = listener.elements(); e.hasMoreElements();) {
				(e.nextElement()).depthFinished(engineResult);
			}
		}
	}

	public void setEngineSettings(EngineSettings engineSettings) {
		this.engineSettings = engineSettings;
	}

	public EngineSettings getEngineSettings() {
		return engineSettings;
	}

	public boolean isRunning() {
		return running;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	public EngineResult getEngineResult() {
		return engineResult;
	}

	private synchronized void checkActive() {
		if (!active) {
			try {
				System.out.println("Engine waiting" + this.toString());
				this.wait();
				System.out.println("Engine continuing");
			} catch (InterruptedException e) {
				e.printStackTrace(); //Shouldn't happen
			}
		}	
	}

	/**
	 * If a player is set to inactive, it cannot output a move until set to inactive.
	 * 
	 * @param active
	 */
	
	public synchronized void setActive(boolean active) {
		if (active)  {
			System.out.println("engine notified " + this);
			this.notifyAll();
		}
		this.active = active;
	}
	
	/**
	 * If a player is killed it will not complete a move.
	 * 
	 */

	public void kill() {
		System.out.println("Engine KILLLED: " + this.toString());
		alive = false;
		running = false; //Speed up death
	}


}
